package com.oet.service.search;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.MultiFieldQueryParser;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser.Operator;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import org.apache.lucene.index.IndexReader;

import com.chenlb.mmseg4j.analysis.MaxWordAnalyzer;

//import com.ydk.book.persistence.entity.Book;

public class SearchEngine {
	private static File BOOK_INDEX = new File("book_index");
	private static final String[] bookSearchFields = {"bookid","title","author","translator",
			"publisher","introduction", "isbn", "content"};
	private static final Logger log = Logger.getLogger(SearchEngine.class.getName());  
	final private static int MAX_RESULT_NUMBER = 50;  
	private Analyzer analyzer; 
	private Directory bookDir; 
	private static Boolean isInitialized = false;
	
	
	/** 
	 * this must be called before everything 
	 * @return
	 * @throws IOException 
	 */
	public void init() throws IOException
	{
		if (!isInitialized)
		{
			log.info("init");
			analyzer = new MaxWordAnalyzer();  
			bookDir = FSDirectory.open(BOOK_INDEX);	
			isInitialized = true;
		}
		
	
//		rebuildBookIndex();
//		rebuildReviewIndex();
//		rebuildActivityIndex();		
	}
	/*
	public void addBook(EpubBook book, String aesKey) throws Exception {
		IndexWriter indexWriter;
		try {				
			indexWriter = new IndexWriter(bookDir, analyzer,false, IndexWriter.MaxFieldLength.UNLIMITED);			
		} catch (CorruptIndexException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (LockObtainFailedException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (IOException e){
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
		Document doc = book.IndexDocument(aesKey);
		indexWriter.deleteDocuments(new Term("bookid", book.getBookUUID()));
		indexWriter.addDocument(doc);
		indexWriter.optimize();
		indexWriter.close();
	}
	
	
	
	public Document IndexDocument(Book book) throws Exception{
		Document doc = new Document();
		//book uuid
		doc.add(new Field("bookid", book.getYdkBookId(), Field.Store.YES, Field.Index.NOT_ANALYZED));
		//book title/name
		 Field f = new Field("title", book.getTitle(), Field.Store.YES, Field.Index.ANALYZED);
		 f.setBoost(100);
		 doc.add(f);
		//book author
		 String author = "";
		 if (book.getAuthor() != null)
		 {
			 author += " " + book.getAuthor().getName();
		 }
		 if (book.getAuthor1() != null)
		 {
			 author += " " +book.getAuthor1().getName();
		 }
		 if (book.getAuthor2() != null)
		 {
			 author += " " +book.getAuthor2().getName();
		 }
		 if (book.getAuthor3() != null)
		 {
			 author += " " +book.getAuthor3().getName();
		 }
		 
		 
		 String field = (author);
		 if (!field.isEmpty())
		 {
			 f = new Field("author", author, Field.Store.YES, Field.Index.ANALYZED);
			 f.setBoost(80);
			 doc.add(f);
		 }
		 
		//book translator
		 String translator = "";
		 if (book.getTranslator() != null)
		 {
			 translator += " " + book.getTranslator().getName();
		 }
		 if (book.getTranslator1() != null)
		 {
			 translator += " " +book.getTranslator1().getName();
		 }
		 if (book.getTranslator2() != null)
		 {
			 translator += " " +book.getTranslator2().getName();
		 }
		 if (book.getTranslator3() != null)
		 {
			 translator += " " +book.getTranslator3().getName();
		 }
		 field = translator;
		 if (!field.isEmpty())
		 {
			 f = new Field("translator", translator, Field.Store.YES, Field.Index.ANALYZED);
			 f.setBoost(70);
			 doc.add(f);
		 }
		 
		//book publisher
		 if (book.getPublisher() != null && book.getPublisher().getName() != null)
		 {
			 f = new Field("publisher", book.getPublisher().getName(), Field.Store.YES, Field.Index.ANALYZED);
			 f.setBoost(60);
			 doc.add(f);
		 }
		 
		//book introduction 
		 if (book.getSummary() != null && !book.getSummary().isEmpty())
		 {
			 f = new Field("introduction", book.getSummary(), Field.Store.YES, Field.Index.ANALYZED);
			 f.setBoost(90);
			 doc.add(f);
		 }
		 
		//book isbn
		 if (book.getIsbn() != null && !book.getIsbn().isEmpty())
		 {
			 f = new Field("isbn", book.getIsbn(), Field.Store.YES, Field.Index.NOT_ANALYZED);
			 f.setBoost(99);
			 doc.add(f);
		 }
		//book content;
		Field content = new Field("content", "", Field.Store.NO, Field.Index.ANALYZED);
		content.setBoost(20);
		doc.add(content);
		return doc;
	}
	
	
	public void addDbBook(Book book) throws Exception {
		if (book == null) return;
		IndexWriter indexWriter;
		try {				
			indexWriter = new IndexWriter(bookDir, analyzer,false, IndexWriter.MaxFieldLength.UNLIMITED);			
		} catch (CorruptIndexException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (LockObtainFailedException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (IOException e){
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
		Document doc = IndexDocument(book);
		indexWriter.deleteDocuments(new Term("bookid", book.getYdkBookId()));
		indexWriter.addDocument(doc);
		indexWriter.optimize();
		indexWriter.close();
	}
	
	
	
	
	

	public void updateBook(EpubBook book, String aesKey) throws Exception {
		IndexWriter indexWriter;
		try {				
			indexWriter = new IndexWriter(bookDir, analyzer,false, IndexWriter.MaxFieldLength.UNLIMITED);			
		} catch (CorruptIndexException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (LockObtainFailedException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (IOException e){
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
		Document doc = book.IndexDocument(aesKey);
		try {
			indexWriter.updateDocument(new Term("bookid", book.getBookUUID()), doc, analyzer);
		} catch (CorruptIndexException e) {
			log.log(Level.WARNING, "CorruptIndexException: "+book.toString());
		} catch (IOException e) {
			log.log(Level.WARNING, "IOException: "+book.toString());
		}
		indexWriter.optimize();
		indexWriter.close();
	}
	

	public void updateBook(String ydkBookId, EpubBook book, String aesKey) throws Exception {
		IndexWriter indexWriter;
		try {				
			indexWriter = new IndexWriter(bookDir, analyzer,false, IndexWriter.MaxFieldLength.UNLIMITED);			
		} catch (CorruptIndexException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (LockObtainFailedException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (IOException e){
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
		Document doc = book.IndexDocument(aesKey);
		try {
			indexWriter.updateDocument(new Term("bookid", ydkBookId), doc, analyzer);
		} catch (CorruptIndexException e) {
			log.log(Level.WARNING, "CorruptIndexException: "+book.toString());
		} catch (IOException e) {
			log.log(Level.WARNING, "IOException: "+book.toString());
		}
		indexWriter.optimize();
		indexWriter.close();
	}
	

	public void updateDbBook(Book book) throws Exception {
		IndexWriter indexWriter;
		try {				
			indexWriter = new IndexWriter(bookDir, analyzer,false, IndexWriter.MaxFieldLength.UNLIMITED);			
		} catch (CorruptIndexException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (LockObtainFailedException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (IOException e){
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
		Document doc = IndexDocument(book);
		try {
			indexWriter.updateDocument(new Term("bookid", book.getYdkBookId()), doc, analyzer);
		} catch (CorruptIndexException e) {
			log.log(Level.WARNING, "CorruptIndexException: "+book.toString());
		} catch (IOException e) {
			log.log(Level.WARNING, "IOException: "+book.toString());
		}
		indexWriter.optimize();
		indexWriter.close();
	}
	*/
	
	/**
	 * @param key
	 * @throws CorruptIndexException
	 * @throws LockObtainFailedException
	 * @throws IOException
	 */
	public void removeBook(String key) throws CorruptIndexException, LockObtainFailedException, IOException {
		IndexWriter indexWriter;
		try {				
			indexWriter = new IndexWriter(bookDir, analyzer,false, IndexWriter.MaxFieldLength.UNLIMITED);			
		} catch (CorruptIndexException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (LockObtainFailedException e) {
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		} catch (IOException e){
			indexWriter = new IndexWriter(bookDir, analyzer,true, IndexWriter.MaxFieldLength.UNLIMITED);
		}
		indexWriter.deleteDocuments(new Term("bookid", key));
		indexWriter.close();

	}
	
	public List<String> searchBookExact(String keywords, String[] fields) {
		List<String> books = new ArrayList<String>();
		if (fields == null)
		{
			fields = bookSearchFields;
		}
		IndexSearcher indexSearcher;
		try {
			indexSearcher = new IndexSearcher(bookDir);
		} catch (CorruptIndexException e1) {
			log.log(Level.WARNING, "CorruptIndexException: "+bookDir);
			return books;
		} catch (IOException e1) {
			log.log(Level.WARNING, "IOException: "+bookDir);
			return books;
		} 			
	    MultiFieldQueryParser mfqp = new MultiFieldQueryParser(Version.LUCENE_CURRENT, fields, analyzer);
	    mfqp.setDefaultOperator(Operator.AND);
	    try {
			Query query = mfqp.parse(keywords);
			log.info(query.toString());
			TopDocs docs = indexSearcher.search(query, MAX_RESULT_NUMBER);
			log.info("got "+docs.totalHits+" hits");			  
            for(ScoreDoc sd : docs.scoreDocs) { 
            	log.info("title: "+indexSearcher.doc(sd.doc).get("title")+" score: "+sd.score);
            	books.add(indexSearcher.doc(sd.doc).get("bookid"));                 
            }  
		} catch (ParseException e) {
			log.log(Level.WARNING, "search book ParseException: "+keywords+" fields: "+fields);
		} catch (IOException e) {
			log.log(Level.WARNING, "search book IOException: "+keywords+" fields: "+fields);
		}
		return books;
	}
	
	public void clearRepeatedIndex()
	{
		try {
//			File BOOK_INDEX = new File("O:\\galileo\\book_index");

			if (BOOK_INDEX.exists()) {
				Directory bookDir = FSDirectory.open(BOOK_INDEX);
				IndexReader indexReader = IndexReader.open(bookDir, false);
				log.log(Level.WARNING,"删除前索引内的文档数量：" + indexReader.numDocs());

				HashSet hashSet = new HashSet();
				for (int i = 0; i < indexReader.maxDoc(); i++) {
					Field field = indexReader.document(i).getField("bookid");
					if (field != null) {
						String value = field.stringValue();
						if (hashSet.contains(value)) {
							indexReader.deleteDocument(i);
						} else {
							hashSet.add(value);
						}
					}
				}
				indexReader.close();

				indexReader = IndexReader.open(bookDir, false);
				log.log(Level.WARNING,"删除后索引内的文档数量：" + indexReader.numDocs());
				indexReader.close();
			}
			else {
				log.log(Level.WARNING,"索引文件不存在");
			}

		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	
	public void clearBookIndex()
	{
		String[] files = BOOK_INDEX.list();
		for(String file:files)
		{
			new File(file).delete();
		}				
	}
	
//	/**
//	 * @param keyword
//	 * @param field
//	 * @return
//	 */
//	public List<String> searchBookFuzzy(String keyword, String field)
//	{
//		log.info("search book fuzzy: "+keyword+" field: "+field);
//		List<String> books = new ArrayList<String>();
//		IndexSearcher indexSearcher;
//		try {
//			indexSearcher = new IndexSearcher(bookDir);
//			
//		} catch (CorruptIndexException e1) {
//			log.log(Level.WARNING, "CorruptIndexException: "+bookDir);
//			return books;
//		} catch (IOException e1) {
//			log.log(Level.WARNING, "IOException: "+bookDir);
//			return books;
//		} 			
//		FuzzyQuery query=new FuzzyQuery(new Term(field,keyword));
//		try {
//			TopDocs docs = indexSearcher.search(query, MAX_RESULT_NUMBER);
//			log.info("got "+docs.totalHits+" hits");
//			for(ScoreDoc sd : docs.scoreDocs) { 
//				log.info("key: "+indexSearcher.doc(sd.doc).get("key")+" score: "+sd.score);
//				books.add(indexSearcher.doc(sd.doc).get("key"));                 
//			}  
//		} catch (IOException e) {
//			return books;
//		}
//		return books;
//	}
	


}
